<template>
  <div>
    <div class="n-layout-page-header">
      <n-card :bordered="false">
        <BasicForm @register="register" @submit="handleSubmit" @reset="handleReset">
          <template #statusSlot="{ model, field }">
            <n-input v-model:value="model[field]" />
          </template>
        </BasicForm>
      </n-card>
    </div>

    <n-card :bordered="false" class="mt-4 proCard">
      <div style="margin-bottom: 15px;">
        <n-button type="primary" :on-click="handleOpenDrawer.bind(null, {}, true)">
          <template #icon>
            <n-icon>
              <PlusOutlined />
            </n-icon>
          </template>
          添加任务
        </n-button>
      </div>
      <n-data-table :loading="pageLoading" :columns="jobColumns" :data="tableData" :bordered="false"></n-data-table>
      <div class="custom-page" style="margin-top: 15px;">
        <n-pagination v-model:page="index" :page-count="totalPages" @change="updatePage" />
      </div>
    </n-card>

    <n-modal v-model:show="showParamwModal" :show-icon="false" preset="dialog" title="参数运行">
      <div style="margin-bottom: 15px;">
        <n-input v-model:value="instanceParams" type="textarea" placeholder="请输入JSON格式的参数" />
      </div>
      <div style="justify-content: end;display: flex;">
        <n-space>
          <n-button ghost icon-placement="left" @click="handleParamCloseModel">
            取消
          </n-button>
          <n-button type="primary" :loading="formBtnLoading" @click="confirmParamForm">提交</n-button>
        </n-space>
      </div>
    </n-modal>

    <!-- 新增或编辑 -->
    <n-drawer v-model:show="showDrawer" :width="720">
      <n-drawer-content :title="editTitle" :native-scrollbar="false">
        <n-form :model="modifiedJobForm" :rules="rules" ref="formRef" label-placement="top" :label-width="120"
          class="py-4">
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="12" label="任务名称" path="jobName">
              <n-input maxlength="100" show-count placeholder="请输入任务名称" v-model:value="modifiedJobForm.jobName" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="任务描述" path="jobDescription">
              <n-input maxlength="150" show-count placeholder="请输入任务描述" v-model:value="modifiedJobForm.jobDescription" />
            </n-form-item-gi>
            <n-form-item-gi :span="24" label="任务参数" path="jobParams">
              <n-input maxlength="300" show-count type="textarea" :autosize="{
                minRows: 3,
                maxRows: 5
              }" v-model:value="modifiedJobForm.jobParams" placeholder="请输入任务参数" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="定时信息" path="timeExpressionType">
              <n-select v-model:value="modifiedJobForm.timeExpressionType" placeholder="请选择定时信息"
                :options="timeExpressionTypeOptions" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="表达式" path="timeExpression">
              <n-input maxlength="150" show-count placeholder="请配置表达式" v-model:value="modifiedJobForm.timeExpression" />
            </n-form-item-gi>
            <n-form-item-gi :span="24" label="生命周期" path="lifeCycleConvert">
              <n-date-picker style="width: 100%;" v-model:value="modifiedJobForm.lifeCycleConvert" type="datetimerange" />
            </n-form-item-gi>
          </n-grid>
          <n-divider title-placement="left">执行配置</n-divider>
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="12" label="执行类型" path="executeType">
              <n-select v-model:value="modifiedJobForm.executeType" placeholder="请选择执行类型" :options="executeTypeOptions" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="处理器类型" path="processorType">
              <n-select v-model:value="modifiedJobForm.processorType" placeholder="请配置处理器"
                :options="processorTypeOptions" />
            </n-form-item-gi>
            <n-form-item-gi :span="24" label="执行全限定类名（外建容器ID#全限定类名）" path="processorInfo">
              <n-input maxlength="150" show-count placeholder="执行全限定类名（外建容器ID#全限定类名）"
                v-model:value="modifiedJobForm.processorInfo" />
            </n-form-item-gi>
          </n-grid>
          <n-divider title-placement="left">运行时配置</n-divider>
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="12" label="分发类型" path="dispatchStrategy">
              <n-select v-model:value="modifiedJobForm.dispatchStrategy" placeholder="请配置分发类型"
                :options="dispatchStrategy" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="最大实例数" path="maxInstanceNum">
              <n-input-number style="width: 100%;" placeholder="请配置最大实例数"
                v-model:value="modifiedJobForm.maxInstanceNum" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="单机线程并发度" path="concurrency">
              <n-input-number style="width: 100%;" placeholder="请配置单机线程并发度" v-model:value="modifiedJobForm.concurrency" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="运行时间限制（毫秒）" path="instanceTimeLimit">
              <n-input-number style="width: 100%;" placeholder="请配置运行时间限制（毫秒）"
                v-model:value="modifiedJobForm.instanceTimeLimit" />
            </n-form-item-gi>
          </n-grid>
          <n-divider title-placement="left">重试配置</n-divider>
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="12" label="Instance重试次数" path="instanceRetryNum">
              <n-input-number style="width: 100%;" placeholder="请配置Instance重试次数"
                v-model:value="modifiedJobForm.instanceRetryNum" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="Task重试次数" path="taskRetryNum">
              <n-input-number style="width: 100%;" placeholder="请配置Task重试次数"
                v-model:value="modifiedJobForm.taskRetryNum" />
            </n-form-item-gi>
          </n-grid>
          <n-divider title-placement="left">机器配置</n-divider>
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="12" label="最低CPU核心数" path="minCpuCores">
              <n-input-number style="width: 100%;" placeholder="请配置最低CPU核心数"
                v-model:value="modifiedJobForm.minCpuCores" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="最低内存(GB)" path="minMemorySpace">
              <n-input-number style="width: 100%;" placeholder="请配置最低内存(GB)"
                v-model:value="modifiedJobForm.minMemorySpace" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="最低磁盘空间(GB)" path="minDiskSpace">
              <n-input-number style="width: 100%;" placeholder="请配置最低磁盘空间(GB)"
                v-model:value="modifiedJobForm.minDiskSpace" />
            </n-form-item-gi>
          </n-grid>
          <n-divider title-placement="left">集群配置</n-divider>
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="24" label="执行机器地址" path="designatedWorkers">
              <n-input maxlength="300" show-count type="textarea" :autosize="{
                minRows: 3,
                maxRows: 5
              }" v-model:value="modifiedJobForm.designatedWorkers" filterable
                placeholder="请输入执行机器地址(可选，不指定表示全部，多个用英文逗号分隔)" />
            </n-form-item-gi>
            <n-form-item-gi :span="24" label="最大执行机器数量" path="maxWorkerCount">
              <n-input-number style="width: 100%;" placeholder="请配置最大执行机器数量"
                v-model:value="modifiedJobForm.maxWorkerCount" />
            </n-form-item-gi>
          </n-grid>
          <n-divider title-placement="left">报警配置</n-divider>
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="12" label="报警通知人员" path="notifyUserIds">
              <n-input maxlength="150" show-count placeholder="请选择报警通知人员" v-model:value="modifiedJobForm.notifyUserIds" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="错误阈值" path="alarmConfig.alertThreshold">
              <n-input-number style="width: 100%;" placeholder="请配置错误阈值"
                v-model:value="modifiedJobForm.alarmConfig.alertThreshold" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="统计窗口(s)" path="alarmConfig.statisticWindowLen">
              <n-input-number style="width: 100%;" placeholder="请配置统计窗口(s)"
                v-model:value="modifiedJobForm.alarmConfig.statisticWindowLen" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="沉默窗口(s)" path="alarmConfig.silenceWindowLen">
              <n-input-number style="width: 100%;" placeholder="请配置沉默窗口(s)"
                v-model:value="modifiedJobForm.alarmConfig.silenceWindowLen" />
            </n-form-item-gi>
          </n-grid>
          <n-divider title-placement="left">日志配置</n-divider>
          <n-grid :cols="24" :x-gap="24">
            <n-form-item-gi :span="12" label="日志类型" path="logConfig.type">
              <n-select v-model:value="modifiedJobForm.logConfig.type" placeholder="请配置日志类型" :options="logType" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="日志级别" path="logConfig.level">
              <n-select v-model:value="modifiedJobForm.logConfig.level" placeholder="请配置日志级别" :options="logLevel" />
            </n-form-item-gi>
            <n-form-item-gi :span="12" label="日志名称" path="logConfig.loggerName">
              <n-input maxlength="150" show-count placeholder="请配置日志名称"
                v-model:value="modifiedJobForm.logConfig.loggerName" />
            </n-form-item-gi>
          </n-grid>
        </n-form>
        <template #footer>
          <n-space>
            <n-button @click="handleCloseDrawer">
              取消
            </n-button>
            <n-button type="primary" :loading="formBtnLoading" @click="confirmJobForm">
              确定
            </n-button>
          </n-space>
        </template>
      </n-drawer-content>
    </n-drawer>
  </div>
</template>

<script lang="ts" setup>
import { reactive, ref, h, onMounted, unref } from 'vue';
import { BasicForm, FormSchema, useForm } from '@/components/Form/index';
import { useMessage, NButton, NDropdown, NSwitch, useDialog, type FormRules } from 'naive-ui';
import { PlusOutlined } from '@vicons/antd';
import { jobList, updateJobEnable, saveJob, jobRun, removeJob } from '@/api/job/job';
import { MossJobEnum } from '@/enums/mossJobEnum';
import { ResultEnum } from '@/enums/httpEnum';
import { dictGroup } from '@/api/system/dict';
import { isJSON } from '@/utils/convert';
const tableData = ref<any[]>([]);
const message = useMessage();
const jobExecutetype = ref<any[]>([]);
const jobProcessortype = ref<any[]>([]);
const showParamwModal = ref<boolean>(false);
const instanceParams = ref<string>('');
const formBtnLoading = ref<boolean>(false);
const currentJobId = ref<string>('');
const dialog = useDialog();
const index = ref<number>(1);
const pageSize = ref<number>(10);
const totalPages = ref<number>(0);
const pagination = ref<any>({});
const pageLoading = ref<boolean>(false);
const showDrawer = ref<boolean>(false);
const editTitle = ref<string>('');
const pageParam = { appId: MossJobEnum.APPID, page: index.value, pageSize: pageSize.value };
const formRef: any = ref(null);
// 时间表达式选择类型
const timeExpressionTypeOptions = [{ value: "API", label: "API" }, { value: "CRON", label: "CRON" }, { value: "FIXED_RATE", label: '固定频率（毫秒）' }, { value: "FIXED_DELAY", label: '固定延迟（毫秒）' }, { value: "DAILY_TIME_INTERVAL", label: '每日固定间隔' }];
// 处理器类型
const processorTypeOptions = [{ value: "BUILT_IN", label: '内建' }, { value: "EXTERNAL", label: '外置（动态加载）' }];
// 执行方式类型
const executeTypeOptions = [{ value: "STANDALONE", label: '单机执行' }, { value: "BROADCAST", label: '广播执行' }, { value: "MAP", label: 'MAP执行' }, { value: "MAP_REDUCE", label: 'MapReduce执行' }];
// 日志级别
const logLevel = [{ value: 1, label: 'DEBUG' }, { value: 2, label: 'INFO' }, { value: 3, label: 'WARN' }, { value: 4, label: 'ERROR' }, { value: 99, label: 'OFF' }];
// 日志类型
const logType = [{ value: 1, label: 'ONLINE' }, { value: 2, label: 'LOCAL' }, { value: 3, label: 'STDOUT' }, { value: 4, label: 'LOCAL_AND_ONLINE' }, { value: 999, label: 'NULL' }];
// 分发类型
const dispatchStrategy = [{ value: 'HEALTH_FIRST', label: 'HEALTH_FIRST' }, { value: 'RANDOM', label: 'RANDOM' }];

const rules: FormRules = {
  jobName: {
    required: true,
    trigger: ['blur', 'input'],
    message: '请输任务名称',
  },
  timeExpressionType: {
    required: true,
    trigger: ['blur', 'change'],
    message: '请选择定时信息'
  },
  executeType: {
    required: true,
    trigger: ['blur', 'change'],
    message: '请选择执行类型',
  },
  processorType: {
    required: true,
    trigger: ['blur', 'change'],
    message: '请选择处理器类型',
  },
  processorInfo: {
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入执行全限定类名',
  },
  dispatchStrategy: {
    required: true,
    trigger: ['blur', 'change'],
    message: '请选择分发类型',
  },
  maxInstanceNum: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入最大实例数',
  },
  concurrency: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入单机线程并发度',
  },
  instanceTimeLimit: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入运行时间限制（毫秒）',
  },
  instanceRetryNum: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入Instance重试次数',
  },
  taskRetryNum: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入Task重试次数',
  },
  minCpuCores: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入最低CPU核心数',
  },
  minMemorySpace: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入最低内存(GB)',
  },
  minDiskSpace: {
    type: 'number',
    required: true,
    trigger: ['blur', 'input'],
    message: '请输入最低磁盘空间(GB)',
  },
  alarmConfig: {
    alertThreshold: {
      type: 'number',
      required: true,
      trigger: ['blur', 'input'],
      message: '请选输入错误阈值',
    },
    statisticWindowLen: {
      type: 'number',
      required: true,
      trigger: ['blur', 'input'],
      message: '请输入统计窗口(s)',
    },
    silenceWindowLen: {
      type: 'number',
      required: true,
      trigger: ['blur', 'input'],
      message: '请输入沉默窗口(s)',
    },
  }
};
const defaultValueRef = () => ({
  id: undefined,
  jobName: "",
  jobDescription: "",
  appId: MossJobEnum.APPID,
  jobParams: "",
  timeExpressionType: "",
  timeExpression: "",
  executeType: "",
  processorType: "",
  processorInfo: "",
  maxInstanceNum: 0,
  concurrency: 5,
  instanceTimeLimit: 0,
  instanceRetryNum: 0,
  taskRetryNum: 1,
  dispatchStrategy: undefined,

  minCpuCores: 0,
  minMemorySpace: 0,
  minDiskSpace: 0,

  enable: true,
  designatedWorkers: "",
  maxWorkerCount: 0,
  notifyUserIds: [],
  lifeCycle: {
    start: Date.now(),
    end: Date.now(),
  },
  alarmConfig: {
    alertThreshold: 0,
    statisticWindowLen: 0,
    silenceWindowLen: 0
  },
  logConfig: {
    type: 1,
    level: undefined,
    loggerName: undefined
  },
  lifeCycleConvert: [1183135260000, 4102415999000],
});

let modifiedJobForm = reactive({ ...defaultValueRef() });

const handleOpenDrawer = async (row: any, isAdd: boolean) => {
  modifiedJobForm = Object.assign(unref(modifiedJobForm), defaultValueRef());
  showDrawer.value = true;
  editTitle.value = isAdd ? '新增任务' : `编辑任务${row.jobName}`;
  if (!isAdd) {
    modifiedJobForm = Object.assign(modifiedJobForm, row, { lifeCycleConvert: [row.lifeCycle.start, row.lifeCycle.end] });
  }
}

const handleCloseDrawer = () => {
  modifiedJobForm = Object.assign(unref(modifiedJobForm), defaultValueRef());
  showDrawer.value = false;
}

const confirmJobForm = async () => {
  formRef.value.validate(async (errors) => {
    if (!errors) {
      const lifeCycle = { start: modifiedJobForm.lifeCycleConvert[0], end: modifiedJobForm.lifeCycleConvert[1] };
      modifiedJobForm.lifeCycle = lifeCycle;
      formBtnLoading.value = true;
      const data = await saveJob(modifiedJobForm);
      if (data.code === ResultEnum.SUCCESS) {
        message.success("处理成功");
        handleCloseDrawer();
        formBtnLoading.value = false;
        jobListApi(pageParam);
      } else {
        const resultMsg = data.data ? data.data : '';
        message.error(data.mesg + '-' + resultMsg);
        formBtnLoading.value = false;
      }
    } else {
      message.error('验证失败，请填写完整信息');
    }
  });
}
const jobColumns = [
  {
    title: '任务ID',
    key: 'id'
  },
  {
    title: '任务名称',
    key: 'jobName'
  },
  {
    title: '定时信息',
    key: 'timeExpression'
  },
  {
    title: '执行类型',
    key: 'executeType',
    render(row) {
      return jobExecutetype.value.find((item) => item.value === row.executeType)?.label;
    }
  },
  {
    title: '处理器类型',
    key: 'processorType',
    render(row) {
      return jobProcessortype.value.find((item) => item.value === row.processorType)?.label;
    }
  },
  {
    title: '最大实例数',
    key: 'maxInstanceNum'
  },
  {
    title: 'Instance重试次数',
    key: 'instanceRetryNum'
  },
  {
    title: 'Task重试次数',
    key: 'taskRetryNum'
  },
  {
    title: '状态',
    key: 'enable',
    render(row) {
      return [
        h(
          NSwitch,
          {
            size: 'small',
            value: row.enable,
            onUpdateValue(v) {
              row.enable = v;
              console.log(row);
              updateJobEnableApi(row);
            }
          }
        )
      ]
    }
  },
  {
    title: '操作',
    key: 'actions',
    render(row) {
      return [
        h(
          NButton,
          {
            quaternary: true,
            size: 'small',
            type: 'primary',
            onClick: () => handleOpenDrawer(row, false),
          },
          { default: () => '编辑' }
        ),
        h(
          NButton,
          {
            quaternary: true,
            size: 'small',
            type: 'success',
            onClick: () => jobRunApi(row)
          },
          { default: () => '运行' }
        ),
        h(
          NDropdown,
          {
            trigger: "hover",
            options: [
              { label: '参数运行', key: 'PARAM_RUN' },
              { label: '删除任务', key: 'DELETE' }
            ],
            onSelect: (key) => {
              if (key === 'PARAM_RUN') {
                handleParamOpenModel(row);
              }
              if (key === 'DELETE') {
                handleDelete(row);
              }
            },
            size: 'medium',
          },
          {
            default: () => h(NButton, {
              quaternary: true,
              size: 'small',
              type: 'error',
            }, { default: () => '更多' })
          }
        ),
      ]
    }
  },
];

/**
 * 查询
 */
const handleSubmit = () => {
  jobListApi(pageParam);
}
/**
 * 重置查询
 */
function handleReset() {
  jobListApi(pageParam);
}
/**
 * 表格查询参数定义
 */
const schemas: FormSchema[] = [
  {
    field: 'keyword',
    component: 'NInput',
    label: '任务名称',
    componentProps: {
      placeholder: '请输入任务名称',
    },
  },
];

/**
 * 表格查询注册
 */
const [register, { getFieldsValue }] = useForm({
  gridProps: { cols: '1 s:1 m:2 l:3 xl:4 2xl:4' },
  labelWidth: 80,
  schemas,
});

/**
 * 修改任务状态
 */
const updateJobEnableApi = async (row) => {
  console.log(row.enable);
  const data = !row.enable ? await updateJobEnable({ jobId: row.id }) : await saveJobApi(row);
  if (data.code === ResultEnum.SUCCESS) {
    message.success("处理成功");
    jobListApi(pageParam);
  } else {
    const resultMsg = data.data ? data.data : '';
    message.error(data.mesg + '-' + resultMsg);
  }
}

/**
 * 保存任务
 * @param row 
 */
const saveJobApi = (row) => {
  row.enable = true;
  return saveJob(row);
}
/**
 * 运行任务
 * @param row 
 */
const jobRunApi = async (row) => {
  const data = await jobRun({ jobId: row.id, appId: MossJobEnum.APPID });
  if (data.code === ResultEnum.SUCCESS) {
    message.success("处理成功");
    jobListApi(pageParam);
  } else {
    const resultMsg = data.data ? data.data : '';
    message.error(data.mesg + '-' + resultMsg);
  }
}
/**
 * 关闭参数运行
 */
const handleParamCloseModel = () => {
  showParamwModal.value = false;
  instanceParams.value = '';
}

/**
 * 打开参数运行
 */
const handleParamOpenModel = (row) => {
  showParamwModal.value = true;
  currentJobId.value = row.id;
}

/**
 * 确认参数运行
 */
const confirmParamForm = async () => {
  if (!isJSON(instanceParams.value)) {
    message.error("参数格式错误,请输入JSON格式的参数");
    return;
  }
  formBtnLoading.value = true;
  const data = await jobRun({ jobId: currentJobId.value, appId: MossJobEnum.APPID, instanceParams: instanceParams.value });
  if (data.code === ResultEnum.SUCCESS) {
    message.success("处理成功");
    formBtnLoading.value = false;
    instanceParams.value = '';
    showParamwModal.value = false;
  } else {
    formBtnLoading.value = false;
    const resultMsg = data.data ? data.data : '';
    message.error(data.mesg + '-' + resultMsg);
  }
}

/**
 * 删除确认
 * @param record 
 */
const handleDelete = (row) => {
  dialog.warning({
    title: '警告',
    content: `确定删除${row.jobName}吗？`,
    positiveText: '确定',
    negativeText: '取消',
    onPositiveClick: async () => {
      const data = await removeJob({ jobId: row.id });
      if (data.code !== ResultEnum.SUCCESS) {
        const resultMsg = data.data ? data.data : '';
        message.error(data.mesg + '-' + resultMsg);
      }
      if (data.code === ResultEnum.SUCCESS) {
        message.success('处理成功');
        jobListApi(pageParam);
      }
    },
  })
}

/**
 * 任务列表查询
 */
const jobListApi = async (res) => {
  pageLoading.value = true;
  res.index = res.page - 1;
  const data = await jobList({ ...getFieldsValue(), ...res });
  tableData.value = data.data;
  totalPages.value = data.totalPages;
  index.value = data.index + 1;
  pageSize.value = data.pageSize;
  pageLoading.value = false;
}

//页码切换
const updatePage = (page) => {
  pagination.value.page = page;
  pageParam.page = page;
  jobListApi(pageParam);
}

/**
 *  任务执行类型
 */
const getjobExecutetype = async () => {
  const data = await dictGroup({ dictGroup: 'job_execute_type' });
  const arry = data.map(item => ({ label: item.dictName, value: item.dictValue }));
  jobExecutetype.value = arry;
}

/**
 *  任务处理器类型
 */
const getProcessorType = async () => {
  const data = await dictGroup({ dictGroup: 'processor_type' });
  const arry = data.map(item => ({ label: item.dictName, value: item.dictValue }));
  jobProcessortype.value = arry;
}
onMounted(() => {
  jobListApi(pageParam);
  getjobExecutetype();
  getProcessorType();
})
</script>

<style lang="less" scoped></style>
